#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Task.hpp"

class Channel
{
public:
    Channel(int wfd, pid_t id, const std::string& name)
        :_wfd(wfd), _subprocessid(id), _name(name)
    {}
    int GetWfd() { return _wfd; }
    pid_t GetProcessid() { return _subprocessid; }
    std::string GetName() { return _name; }
    void CloseChannel() { close(_wfd); }
    void Wait()
    {
        pid_t rid = waitpid(_subprocessid, nullptr, 0);
        if (rid > 0)
        {
            std::cout << "wait" << rid << " success" << std::endl;
        }
    }
    ~Channel()
    {}
private:
    int _wfd;
    pid_t _subprocessid;
    std::string _name;
};

// void work(int rfd)
// {
//     while (true)
//     {
//         int command = 0;
//         int n = read(rfd, &command, sizeof(command));
//         if (n == sizeof(int))
//         {
//             std::cout << "pid is: " << getpid() << " handler task" << std::endl;
//             ExcuteTask(command);
//         }
//         else if (n == 0)
//         {
//             std::cout << "subprocess: " << getpid() << " quit" << std::endl; 
//             break;
//         }
//     }
// }



//形参类型和命名规范
//const &:输出
//& :输入输出型参数
//* :输出型参数
// task_t task: 回调函数
void CreatChannelAndSub(int num, std::vector<Channel> *channels, task_t task)
{
    //Bug?
    for (int i = 0; i < num; i++)
    {
        // 1.创建管道
        int pipefd[2] = { 0 };
        int n = pipe(pipefd);
        if (n < 0)
            exit(1);
        // 2.创建子进程
        pid_t id = fork();
        if (id == 0)
        {
            if (!channels->empty())
            {
                // 第二次之后创建的进程要关闭之前父进程继承下来的管道写端
                for (auto& channel : *channels)
                    channel.CloseChannel();// 关闭的是当前进程（子进程）的写端，也就是从父进程那得来的文件描述符表
            }
            //child - read
            close(pipefd[1]);
            dup2(pipefd[0], 0); // 将读端重定向到标准输入
            task();
            // work();
            close(pipefd[0]);
            exit(0);
        }
        // 3.构建一个名字
        std::string channel_name = "Channel-" + std::to_string(i);
        //father - write
        close(pipefd[0]);
        // a.子进程的pid，b.父进程关心的管道写端
        channels->push_back(Channel(pipefd[1], id, channel_name));
    }
}

int NextChannel(int channelnum)
{
    static int next = 0;
    int channel = next;
    ++next;
    next %= channelnum;
    return channel;
}

void SendTaskCommand(Channel &channel, int taskcommand)
{
    write(channel.GetWfd(), &taskcommand, sizeof(taskcommand));
}

void CtrlProcessOnce(std::vector<Channel>& channels)
{
    // a.选择一个任务
    int taskcommand = SelectTask();
    // b.选择一个信道进程
    int channelindex = NextChannel(channels.size());
    // c.发送任务
    SendTaskCommand(channels[channelindex], taskcommand);
    std::cout << std::endl;
    std::cout << "taskcommand: " << taskcommand << " channel: " << channels[channelindex].GetName()\
        << " subprocess: " << channels[channelindex].GetProcessid() << std::endl;
}

void CtrlProcess(std::vector<Channel>& channels, int times = -1)
{
    if (times > 0)
    {
        while (times--)
        {
            CtrlProcessOnce(channels);
            sleep(1);
        }
    }
    else
    {
        while (true)
        {
            CtrlProcessOnce(channels);
            sleep(1);
        }
    }
}

void CleanUpChannel(std::vector<Channel>& channels)
{
    // int num = channels.size() - 1;
    // while (num >= 0)
    // {
    //     channels[num].CloseChannel();
    //     channels[num--].Wait();
    // }
    for (auto& channel : channels)
    {
        channel.CloseChannel();
        channel.Wait();
    }
    // //注意
    // for (auto& channel : channels)
    // {
    //     channel.Wait();
    // }
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        std::cerr << "Usage: " << argv[0] << " processnum" << std::endl;
        return 1;
    }
    int num = std::stoi(argv[1]);
    LoadTask();
    std::vector<Channel> channels;
    // 1.创建信道和子进程
    CreatChannelAndSub(num, &channels, work);

    // 2.通过channel控制子进程
    CtrlProcess(channels, 10);
    
    // 3.回收管道和子进程 a.关闭所有写端，b.回收子进程
    CleanUpChannel(channels);

    return 0;
}